package com.intretech.umsin.oss.util;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

/**
 * @version V1.0
 * @ClassName: SftpOssClient
 * @Description: SFTP 连接客户端类
 * @author: 李启联
 * @date: 2020-04-02 11:26
 * @Copyright: Copyright (c), 2020
 */
@Slf4j
public class SftpOssClient {

    private String host, username, password, workDir,privateKey,privateKeyPassword;
    private int port;
    private boolean usePassword=true,isWorkDirNotEmpty=false;

    private JSch jsch ;
    ThreadLocal<ChannelSftp> clientLocal = new ThreadLocal<ChannelSftp>();
    /**
     * 打开stfp 通道
     *
     * @return sftp通道
     * @throws Exception
     */
    private ChannelSftp getSftpChannel() throws Exception {
        ChannelSftp local = clientLocal.get();
        if(local==null) {
            Session session = jsch.getSession(username, host, port);
            if(usePassword) {
                session.setPassword(password);
            }else {
                jsch.addIdentity(privateKey, privateKeyPassword);
            }
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setTimeout(3000);
            session.setConfig(config);
            session.connect();
            ChannelSftp channel = (ChannelSftp)session.openChannel("sftp");
            channel.connect();
            if(isWorkDirNotEmpty) {
                channel.cd(workDir);
            }
            clientLocal.set(channel);
            return  channel;
        }else {
            return local;
        }
    }

    public SftpOssClient(String host, int port, String username, String password, String workDir, String privateKey, String privateKeyPassword) {
        super();
        this.host = host;
        this.port = port;
        this.username = username;
        this.workDir = workDir;
        this.password=password;
        this.isWorkDirNotEmpty=StringUtils.isNotBlank(workDir);
        if(StringUtils.isNotBlank(privateKey)) {
            this.privateKey=privateKey;
            this.privateKeyPassword=privateKeyPassword;
            usePassword=false;
        }
        jsch = new JSch(); // 创建JSch对象
    }

    public void upload(InputStream input, String dest) throws Exception {
        ChannelSftp channel = getSftpChannel();
        String pathName = "",fileName=dest;
        if(dest.lastIndexOf("/")>0) {
            pathName = dest.substring(0, dest.lastIndexOf("/"));
            fileName = dest.substring(dest.lastIndexOf("/") + 1);
        }
        boolean create = createDirs(pathName, channel);
        if(create) {
            channel.put(input, fileName);
        }
    }

    public void upload(String src, String dest) throws Exception {
        ChannelSftp channel = getSftpChannel();
        String pathName = "",fileName=dest;
        if(dest.lastIndexOf("/")>0) {
            pathName = dest.substring(0, dest.lastIndexOf("/"));
            fileName = dest.substring(dest.lastIndexOf("/") + 1);
        }
        boolean create = createDirs(pathName, channel);
        if(create) {
            channel.put(src, fileName);
        }
    }

    public void download(String src, String dest) throws Exception {
        ChannelSftp channel = getSftpChannel();
        channel.get(src, dest);
    }

    public void download(String src, OutputStream out) throws Exception {
        ChannelSftp channel = getSftpChannel();
        channel.get(src, out);
    }

    /**
     * 下载文件
     * @param src sftp 源文件
     * @return
     * @throws Exception
     */
    public byte[] download(String src) throws Exception {
        //OutputStream outputStream = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ChannelSftp channel = getSftpChannel();
            String pathName = "",fileName=src;
            if(src.lastIndexOf("/")>0) {
                pathName = src.substring(0, src.lastIndexOf("/"));
                fileName = src.substring(src.lastIndexOf("/") + 1);
            }
            channel.cd(pathName);
            //File file = new File(fileName);
            //outputStream = new FileOutputStream(file);
            channel.get(src, bos);
            log.info("Download file success. TargetPath: {}", src);
            return bos.toByteArray();
        } catch (Exception e) {
            log.error("Download file failure. TargetPath: {}", src, e);
            throw new Exception("Download File failure");
        } finally {
            if (bos != null) {
                bos.close();
            }
        }
    }

    /**
     * 删除指定路径下的文件
     * @param filePathName
     */
    public void deleteFile(String filePathName){
        try {
            ChannelSftp channel = getSftpChannel();
            channel.rm(filePathName);
        } catch (Exception e) {
            log.error("deleteFile {}  异常", filePathName, e);
        }

    }


    public List<String> list(String dir) throws Exception{
        ChannelSftp channel = getSftpChannel();
        Vector<ChannelSftp.LsEntry> ls = channel.ls(dir);
        Iterator<LsEntry> it = ls.iterator();
        List<String> child=new ArrayList<String>();
        while(it.hasNext()){
            LsEntry next = (LsEntry) it.next();
            if(!next.getFilename().equals(".")&&!next.getFilename().equals("..")){//去除无效文件
                child.add(next.getFilename());
            }
        }
        return child;
    }


    /**
     * 递归创建目录
     * @param dirPath
     * @param sftp
     * @return
     */
    private boolean createDirs(String dirPath, ChannelSftp sftp) {
        if (dirPath != null && !dirPath.isEmpty()
                && sftp != null) {
            String[] dirs = Arrays.stream(dirPath.split("/"))
                    .filter(StringUtils::isNotBlank)
                    .toArray(String[]::new);

            for (String dir : dirs) {
                try {
                    sftp.cd(dir);
                    log.info("Change directory {}", dir);
                } catch (Exception e) {
                    try {
                        sftp.mkdir(dir);
                        log.info("Create directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Create directory failure, directory:{}", dir, e1);
                    }
                    try {
                        sftp.cd(dir);
                        log.info("Change directory {}", dir);
                    } catch (SftpException e1) {
                        log.error("Change directory failure, directory:{}", dir, e1);
                    }
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 关闭channel
     *
     * @param
     * @throws Exception
     */
    public void closeChannel() throws Exception {
        ChannelSftp channel = clientLocal.get();
        if (channel != null) {
            channel.disconnect();
            if (channel.getSession() != null) {
                channel.getSession().disconnect();
            }
        }
        clientLocal.remove();
    }

}